package com.xiaoniu.forum.dao.es;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.xiaoniu.forum.common.WithIDEntity;
import com.xiaoniu.forum.common.utils.PageUtils;
import com.xiaoniu.forum.renren.common.exception.RRException;
import lombok.SneakyThrows;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Repository
public class SearchDaoImpl implements SearchDao {
    @Autowired
    private RestHighLevelClient client;

    @Override
    public boolean save(String indexName, String id, Object obj) {
        try {
            IndexRequest indexRequest = new IndexRequest(indexName);
            indexRequest.id(id);
            indexRequest.source(JSON.toJSONString(obj), XContentType.JSON);
            client.index(indexRequest, RequestOptions.DEFAULT);
            return true;
        }catch (Exception e){
            e.printStackTrace();
            throw new RRException("保存文档出现异常");
        }

    }

    @Override
    public boolean bulkSave(String indexName, List<? extends WithIDEntity> entities) {
        try{
            BulkRequest request = new BulkRequest(indexName);
            for (WithIDEntity withIDEntity : entities){
                request.add(new IndexRequest().id(withIDEntity.id()).source(JSON.toJSONString(withIDEntity),XContentType.JSON));
            }
            client.bulk(request,RequestOptions.DEFAULT);
            return true;
        }catch (Exception e) {
            e.printStackTrace();
            throw new RRException("批量保存文档出现异常");
        }
    }

    @Override
    public boolean updateById(String indexName, String id, Object obj){
        try{
            UpdateRequest request = new UpdateRequest();
            request.index(indexName);
            request.id(id);
            request.doc(JSON.toJSONString(obj),XContentType.JSON);
            client.update(request, RequestOptions.DEFAULT);
            return true;
        }catch (Exception e) {
            e.printStackTrace();
            throw new RRException("更新文档出现异常");
        }
    }



    @Override
    public boolean bulkUpdateById(String indexName, List<? extends WithIDEntity> entities) {
        try{
            BulkRequest request = new BulkRequest(indexName);
            for (WithIDEntity withIDEntity : entities){
                request.add(new UpdateRequest().id(withIDEntity.id()).doc(JSON.toJSONString(withIDEntity),XContentType.JSON));
            }
            client.bulk(request,RequestOptions.DEFAULT);
            return true;
        }catch (Exception e) {
            e.printStackTrace();
            throw new RRException("批量更新文档出现异常");
        }

    }

    @Override
    public boolean deleteById(String indexName, String id) {
        try {
            DeleteRequest request = new DeleteRequest(indexName);
            request.id(id);
            client.delete(request, RequestOptions.DEFAULT);
            return true;
        }catch (Exception e){
            e.printStackTrace();
            throw new RRException("删除文档出现异常");
        }
    }

    @Override
    public <T> T findById(String indexName, String id, Class<T> clazz) {
        try {
            GetRequest request = new GetRequest(indexName);
            request.id(id);
            GetResponse res = client.get(request, RequestOptions.DEFAULT);
            String json = res.getSourceAsString();
            return JSON.parseObject(json,clazz);
        }catch (Exception e){
            e.printStackTrace();
            throw new RRException("查询文档出现异常");
        }
    }

    @Override
    public <T> void search(String indexName, SearchSourceBuilder searchSourceBuilder, PageUtils page, Class<T> clazz) {
        searchSourceBuilder.from((page.getCurrPage()-1)*page.getPageSize());
        searchSourceBuilder.size(page.getPageSize());
        SearchHits hits = getSearchHits(indexName, searchSourceBuilder);
        List<T> list = new ArrayList<>();
        //将记录里的字符串转为对象
        for (SearchHit hit:hits){
            String docment = hit.getSourceAsString();
            list.add(JSON.parseObject(docment,clazz));
        }
        page.setList(list);
        setPage(hits,page);
    }

    @Override
    public <T> void highlightSearch(String indexName, SearchSourceBuilder searchSourceBuilder,HighlightBuilder highlightBuilder, PageUtils page, Class<T> clazz) {
        searchSourceBuilder.from((page.getCurrPage()-1)*page.getPageSize());
        searchSourceBuilder.size(page.getPageSize());
        //添加高亮
        searchSourceBuilder.highlighter(highlightBuilder);
        //检索数据
        SearchHits hits = getSearchHits(indexName, searchSourceBuilder);
        //
        List<T> list = new ArrayList<>();
        for (SearchHit hit:hits){
            String docment = hit.getSourceAsString();
            JSONObject jsonObject = JSON.parseObject(docment);
            setHighlightValue(jsonObject,hit.getHighlightFields(),highlightBuilder);
            list.add(jsonObject.toJavaObject(clazz));
        }
        page.setList(list);
        setPage(hits,page);
    }

    /**
     *   将字段的值设置为对应高亮的值
     */
    private void setHighlightValue(JSONObject jsonObject, Map<String, HighlightField> highlightFieldMap, HighlightBuilder highlightBuilder){
        //1、遍历高亮的所有字段
        for (HighlightBuilder.Field field:highlightBuilder.fields()){
            String fieldName = field.name();
            //2、拿到每个高亮字段对应的值
            HighlightField highlightField = highlightFieldMap.get(fieldName);
            if (highlightField == null){//如果高亮字段结果集没有，那么就不需要设置值了，直接结束
                continue;
            }
            String value = highlightField.getFragments()[0].string();
            //3、设置值
            jsonObject.put(fieldName,value);
        }
    }

    /**
     *   获取检索结果集
     */
    private SearchHits getSearchHits(String indexName, SearchSourceBuilder searchSourceBuilder){
        try {
            SearchRequest request = new SearchRequest(indexName);
            request.source(searchSourceBuilder);
            SearchResponse res = client.search(request, RequestOptions.DEFAULT);
            SearchHits hits = res.getHits();
            return hits;
        }catch (Exception e){
            e.printStackTrace();
            throw new RRException("检索文档出现异常");
        }
    }

    /**
     *   根据结果集，设置分页对象的各属性
     */
    private void setPage(SearchHits hits,PageUtils page){
        page.setCurrPage(page.getCurrPage()+1);
        int totalCount = (int) hits.getTotalHits().value;
        page.setTotalCount(totalCount);
        if(totalCount % page.getPageSize() == 0){
            page.setTotalPage(totalCount/page.getPageSize());
        }else{
            page.setTotalPage(totalCount/page.getPageSize()+1);
        }
    }
}
